/**************************************************************************************
*
*	Copyright (C) 2009 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	Renderer.cpp
*
*	Comments	-	See Renderer.h
*
**************************************************************************************/
#include "../Include/Renderer.h"
#include "../Include/GraphicsDevice.h"
#include "../Include/WindowApplicationWin32.h"
#include "../Include/SwapChain.h"
#include "../Include/GraphicsResourceManager.h"
#include "../Include/GraphicsBuffer.h"
#include "../Include/EffectsLibrary.h"
#include "../Include/Model.h"
#include "../Include/String.h"

namespace Pulse
{

	void RendererDesc::SetAsImmediate( ERendererProcessingType::Type	_processingType, UINT _adapter, UINT _adapterOutput, BOOL _bDebug,
		BOOL _bSoftwareRendererFallback, DisplayMode _displaySettings )
	{
		processingType = _processingType;
		deviceContextType = EDeviceContext::IMMEDIATE;
		adapter = _adapter;
		adapterOutput = _adapterOutput;
		bDebug = _bDebug;
		bSoftwareRendererFallback = _bSoftwareRendererFallback;
		displaySettings = _displaySettings;
	}

	void RendererDesc::SetAsDeferred( void )
	{
		deviceContextType = EDeviceContext::DEFERRED;
	}

	Renderer::Renderer( void )
		: m_pDevice( PSX_NULL ), m_pDeviceContext( PSX_NULL ),
		m_pPipeline( PSX_NULL ), m_pEffectsLibrary( PSX_NULL )
	{

	}

	Renderer::~Renderer( void )
	{
	}

	EErrorCode::Type Renderer::Initialize( RendererDesc  *pDesc )
	{
		EErrorCode::Type ret;
		//HRESULT hr;
		m_desc = *pDesc;

		// Get graphics device
		m_pDevice = GraphicsDevice::GetInstance();
		
		// We can only have one immediate renderer. The rest are created with
		//	a deferred renderer context
		if ( m_desc.deviceContextType == EDeviceContext::IMMEDIATE )
		{
			if ( m_pDevice->GetNakedDevice() != PSX_NULL )
			{
				 PSX_PushError( "Immediate renderer already created." );
				return EErrorCode::GRAPHICS;
			}

			// Initialize graphics device for the first time.
			if ( (ret = m_pDevice->Initialize( &m_desc )) != EErrorCode::OKAY )
			{
				PSX_SafeDelete( m_pDevice );
				return ret;
			}

			m_pDeviceContext = (ID3D11DeviceContext*)m_pDevice->GetNakedDeviceContext();

			// Initialize graphics pipeline
			m_pPipeline = new GraphicsPipeline( this );

			m_pPipeline->SetInputAssemblerStage( new InputAssemblerStage );
			m_pPipeline->SetVertexShaderStage( new VertexShaderStage );
			m_pPipeline->SetRasterizerStage( new RasterizerStage );
			m_pPipeline->SetPixelShaderStage( new PixelShaderStage );
			m_pPipeline->SetOutputMergerStage( new OutputMergerStage );
			m_pPipeline->SetComputeShaderStage( new ComputeShaderStage );

		}
		else
		{	
			// Create deferred device renderer context
			// TODO: this is not really what we want. Our version of deferred renderer should just
			//	create a render queue to be passed to the immediate renderer for actual rendering.
			
			ID3D11Device *pDevice = (ID3D11Device*)m_pDevice->GetNakedDevice();
			
			// Check if we already have an immediate context made
			if ( pDevice == PSX_NULL )
			{
				PSX_PushError( "Creating a deferred renderer withouth an Immediate render context. Create immediate render context first." );
				return EErrorCode::GRAPHICS;
			}

			HRESULT result = pDevice->CreateDeferredContext( 0, &m_pDeviceContext );

			switch( result )
			{
			case DXGI_ERROR_DEVICE_REMOVED:
				PSX_PushError( " if the video card has been physically removed from the system, or a driver upgrade for the video card has occurred. If this error occurs, you should destroy and recreate the device." );
				break;
			case DXGI_ERROR_INVALID_CALL:
				PSX_PushError( " the CreateDeferredContext method cannot be called from the current context. For example, if the device was created with the D3D11_CREATE_DEVICE_SINGLETHREADED value, CreateDeferredContext returns DXGI_ERROR_INVALID_CALL." );
				break;
			case E_INVALIDARG:
				PSX_PushError( "the ContextFlags parameter is invalid." );
				break;
			case E_OUTOFMEMORY:
				PSX_PushError( "the application has exhausted available memory." );
				break;
			}

		}

		return EErrorCode::OKAY;
	}

	EErrorCode::Type Renderer::InitializeRenderPath( void )
	{
		// TODO: Probably move this inside the renderer creation/initialization
		if ( LoadEffectsLibrary( PSX_String("DefaultEffectsLib.fxl") ) != EErrorCode::OKAY )
		{
			PSX_PushError( "Failed to load default effects library." );
			return EErrorCode::GRAPHICS;
		}

		// Initialize render path.
		// TODO: We ultimately want a flexible render path to be loaded
		//	at initialization time.
		m_renderPath.Initialize();

		return EErrorCode::OKAY;
	}

	void Renderer::Cleanup( void )
	{
		// Release the graphics pipeline stages
		{
			delete m_pPipeline->GetInputAssemblerStage();
			delete m_pPipeline->GetVertexShaderStage();
			delete m_pPipeline->GetRasterizerStage();
			delete m_pPipeline->GetPixelShaderStage();
			delete m_pPipeline->GetOutputMergerStage();
			delete m_pPipeline->GetComputeShaderStage();

			m_pPipeline->SetInputAssemblerStage( PSX_NULL );
			m_pPipeline->SetVertexShaderStage( PSX_NULL );
			m_pPipeline->SetRasterizerStage( PSX_NULL );
			m_pPipeline->SetPixelShaderStage( PSX_NULL );
			m_pPipeline->SetOutputMergerStage( PSX_NULL );
			m_pPipeline->SetComputeShaderStage( PSX_NULL );
		}

		m_renderPath.Cleanup();

		PSX_SafeDelete( m_pEffectsLibrary );
		PSX_SafeDelete( m_pDevice );
	}

	MappedResource Renderer::MapResource( GraphicsBuffer *pBuffer, UINT subresourceIndex, EResourceMapping::Type map, UINT flags )
	{
		D3D11_MAPPED_SUBRESOURCE data;
		MappedResource ret;

		m_pDeviceContext->Map( pBuffer->m_pBuffer, subresourceIndex, DX11Mapping::ToDXMapping( map ), 0, &data );

		ret.pData = (BYTE*)data.pData;
		ret.RowPitch = data.RowPitch;
		ret.DepthPitch = data.DepthPitch;

		return ret;
	}

	void Renderer::UnmapResource( GraphicsBuffer *pBuffer, UINT subresourceIndex )
	{
		m_pDeviceContext->Unmap( pBuffer->m_pBuffer, subresourceIndex );
	}

	EErrorCode::Type Renderer::LoadEffectsLibrary( const CHAR *pFile )
	{
		PSX_SafeDelete( m_pEffectsLibrary );

		// Load effects library
		m_pEffectsLibrary = new EffectsLibrary;

		if ( !m_pEffectsLibrary->Load( pFile, m_renderPath.GetTechniquesLibraryPath() ) )
		{
			PSX_PushError( "Failed to load default effects library." );
			return EErrorCode::GRAPHICS;
		}

		return EErrorCode::OKAY;
	}

	void Renderer::AddToRender( Model *pModel )
	{
		PSX_PushError( "This function is deprecated. Don't use this function." );

		/*const SIZE_T numEntries = pModel->GetNumRenderStageEntries();
		RenderEntry **pEntries = pModel->GetRenderStageEntries();

		for ( SIZE_T i = 0; i < numEntries; ++i )
		{
			m_renderStageManager.AddRenderEntry( *(pEntries + i) );
		}*/
	}

	void Renderer::AddToRender( RenderEntry *pEntry )
	{
		PSX_PushError( "This function is deprecated." );
		//m_renderStageManager.AddRenderEntry( pEntry );
	}

	void Renderer::BeginRender( void )
	{

	}

	void Renderer::Render( void )
	{

	}

	void Renderer::EndRender( void )
	{

	}

	void Renderer::Present( void )
	{
		ExecuteRenderStages();
	}

	void Renderer::ExecuteRenderStages( void )
	{
		PSX_PushError( "This function, Renderer::ExecuteRenderStages, is deprecated." );
		//m_renderStageManager.ExecuteRenderStages( m_pPipeline );
	}

	void Renderer::ExecuteRenderEntry( RenderEntry *pEntry )
	{
		pEntry->PreExecute( m_pPipeline );
		pEntry->Execute( m_pPipeline );
		pEntry->PostExecute( m_pPipeline );
	}

	void Renderer::DrawIndexed( UINT indexCount, UINT startIndexLocation, INT baseVertexLocation )
	{
		m_pDeviceContext->DrawIndexed( indexCount, startIndexLocation, baseVertexLocation );
	}

	void Renderer::Dispatch( UINT threadCountX, const UINT threadCountY, const UINT threadCountZ )
	{
		m_pDeviceContext->Dispatch( threadCountX, threadCountY, threadCountZ );
	}


}